// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE

#if !defined(JSON_IS_AMALGAMATION)

#include "../json/assertions.h"
#include "../json/value.h"
#include "../json/writer.h"

#endif // if !defined(JSON_IS_AMALGAMATION)

#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <iostream>
#include <sstream>
#include <utility>

// Provide implementation equivalent of std::snprintf for older _MSC compilers
#if defined(_MSC_VER) && _MSC_VER < 1900
#include <stdarg.h>
static int msvc_pre1900_c99_vsnprintf(char* outBuf, size_t size,
                                      const char* format, va_list ap) {
  int count = -1;
  if (size != 0)
    count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
  if (count == -1)
    count = _vscprintf(format, ap);
  return count;
}

int JSON_API msvc_pre1900_c99_snprintf(char* outBuf, size_t size,
                                       const char* format, ...) {
  va_list ap;
  va_start(ap, format);
  const int count = msvc_pre1900_c99_vsnprintf(outBuf, size, format, ap);
  va_end(ap);
  return count;
}
#endif

// Disable warning C4702 : unreachable code
#if defined(_MSC_VER)
#pragma warning(disable : 4702)
#endif

#define JSON_ASSERT_UNREACHABLE assert(false)

namespace Json {
    template<typename T>
    static std::unique_ptr<T> cloneUnique(const std::unique_ptr<T> &p) {
        std::unique_ptr<T> r;
        if (p) {
            r = std::unique_ptr<T>(new T(*p));
        }
        return r;
    }

// This is a walkaround to avoid the static initialization of Value::null.
// kNull must be word-aligned to avoid crashing on ARM.  We use an alignment of
// 8 (instead of 4) as a bit of future-proofing.
#if defined(__ARMEL__)
#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
#else
#define ALIGNAS(byte_alignment)
#endif

// static
    Value const &Value::nullSingleton() {
        static Value const nullStatic;
        return nullStatic;
    }

#if JSON_USE_NULLREF
// for backwards compatibility, we'll leave these global references around, but
// DO NOT use them in JSONCPP library code any more!
// static
    Value const &Value::null = Value::nullSingleton();

// static
    Value const &Value::nullRef = Value::nullSingleton();
#endif

#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
    template <typename T, typename U>
    static inline bool InRange(double d, T min, U max) {
      // The casts can lose precision, but we are looking only for
      // an approximate range. Might fail on edge cases though. ~cdunn
      return d >= static_cast<double>(min) && d <= static_cast<double>(max);
    }
#else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)

    static inline double integerToDouble(Json::UInt64 value) {
        return static_cast<double>(Int64(value / 2)) * 2.0 +
               static_cast<double>(Int64(value & 1));
    }

    template<typename T>
    static inline double integerToDouble(T value) {
        return static_cast<double>(value);
    }

    template<typename T, typename U>
    static inline bool InRange(double d, T min, U max) {
        return d >= integerToDouble(min) && d <= integerToDouble(max);
    }

#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)

/** Duplicates the specified string value.
 * @param value Pointer to the string to duplicate. Must be zero-terminated if
 *              length is "unknown".
 * @param length Length of the value. if equals to unknown, then it will be
 *               computed using strlen(value).
 * @return Pointer on the duplicate instance of string.
 */
    static inline char *duplicateStringValue(const char *value, size_t length) {
        // Avoid an integer overflow in the call to malloc below by limiting length
        // to a sane value.
        if (length >= static_cast<size_t>(Value::maxInt))
            length = Value::maxInt - 1;

        auto newString = static_cast<char *>(malloc(length + 1));
        if (newString == nullptr) {
            throwRuntimeError("in Json::Value::duplicateStringValue(): "
                              "Failed to allocate string value buffer");
        }
        memcpy(newString, value, length);
        newString[length] = 0;
        return newString;
    }

/* Record the length as a prefix.
 */
    static inline char *duplicateAndPrefixStringValue(const char *value,
                                                      unsigned int length) {
        // Avoid an integer overflow in the call to malloc below by limiting length
        // to a sane value.
        JSON_ASSERT_MESSAGE(length <= static_cast<unsigned>(Value::maxInt) -
                                      sizeof(unsigned) - 1U,
                            "in Json::Value::duplicateAndPrefixStringValue(): "
                            "length too big for prefixing");
        size_t actualLength = sizeof(length) + length + 1;
        auto newString = static_cast<char *>(malloc(actualLength));
        if (newString == nullptr) {
            throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): "
                              "Failed to allocate string value buffer");
        }
        *reinterpret_cast<unsigned *>(newString) = length;
        memcpy(newString + sizeof(unsigned), value, length);
        newString[actualLength - 1U] =
                0; // to avoid buffer over-run accidents by users later
        return newString;
    }

    inline static void decodePrefixedString(bool isPrefixed, char const *prefixed,
                                            unsigned *length, char const **value) {
        if (!isPrefixed) {
            *length = static_cast<unsigned>(strlen(prefixed));
            *value = prefixed;
        } else {
            *length = *reinterpret_cast<unsigned const *>(prefixed);
            *value = prefixed + sizeof(unsigned);
        }
    }
/** Free the string duplicated by
 * duplicateStringValue()/duplicateAndPrefixStringValue().
 */
#if JSONCPP_USING_SECURE_MEMORY
    static inline void releasePrefixedStringValue(char* value) {
      unsigned length = 0;
      char const* valueDecoded;
      decodePrefixedString(true, value, &length, &valueDecoded);
      size_t const size = sizeof(unsigned) + length + 1U;
      memset(value, 0, size);
      free(value);
    }
    static inline void releaseStringValue(char* value, unsigned length) {
      // length==0 => we allocated the strings memory
      size_t size = (length == 0) ? strlen(value) : length;
      memset(value, 0, size);
      free(value);
    }
#else  // !JSONCPP_USING_SECURE_MEMORY

    static inline void releasePrefixedStringValue(char *value) { free(value); }

    static inline void releaseStringValue(char *value, unsigned) { free(value); }

#endif // JSONCPP_USING_SECURE_MEMORY

} // namespace Json

// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// ValueInternals...
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
#if !defined(JSON_IS_AMALGAMATION)

#include "json_valueiterator.inl"

#endif // if !defined(JSON_IS_AMALGAMATION)

namespace Json {

#if JSON_USE_EXCEPTION

    Exception::Exception(String msg) : msg_(std::move(msg)) {}

    Exception::~Exception() noexcept = default;

    char const *Exception::what() const noexcept { return msg_.c_str(); }

    RuntimeError::RuntimeError(String const &msg) : Exception(msg) {}

    LogicError::LogicError(String const &msg) : Exception(msg) {}

    JSONCPP_NORETURN void throwRuntimeError(String const &msg) {
        throw RuntimeError(msg);
    }

    JSONCPP_NORETURN void throwLogicError(String const &msg) {
        throw LogicError(msg);
    }

#else // !JSON_USE_EXCEPTION
    JSONCPP_NORETURN void throwRuntimeError(String const& msg) {
      std::cerr << msg << std::endl;
      abort();
    }
    JSONCPP_NORETURN void throwLogicError(String const& msg) {
      std::cerr << msg << std::endl;
      abort();
    }
#endif

// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class Value::CZString
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////

// Notes: policy_ indicates if the string was allocated when
// a string is stored.

    Value::CZString::CZString(ArrayIndex index) : cstr_(nullptr), index_(index) {}

    Value::CZString::CZString(char const *str, unsigned length,
                              DuplicationPolicy allocate)
            : cstr_(str) {
        // allocate != duplicate
        storage_.policy_ = allocate & 0x3;
        storage_.length_ = length & 0x3FFFFFFF;
    }

    Value::CZString::CZString(const CZString &other) {
        cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != nullptr
                 ? duplicateStringValue(other.cstr_, other.storage_.length_)
                 : other.cstr_);
        storage_.policy_ =
                static_cast<unsigned>(
                        other.cstr_
                        ? (static_cast<DuplicationPolicy>(other.storage_.policy_) ==
                           noDuplication
                           ? noDuplication
                           : duplicate)
                        : static_cast<DuplicationPolicy>(other.storage_.policy_)) &
                3U;
        storage_.length_ = other.storage_.length_;
    }

    Value::CZString::CZString(CZString &&other) noexcept
            : cstr_(other.cstr_), index_(other.index_) {
        other.cstr_ = nullptr;
    }

    Value::CZString::~CZString() {
        if (cstr_ && storage_.policy_ == duplicate) {
            releaseStringValue(const_cast<char *>(cstr_),
                               storage_.length_ + 1U); // +1 for null terminating
            // character for sake of
            // completeness but not actually
            // necessary
        }
    }

    void Value::CZString::swap(CZString &other) {
        std::swap(cstr_, other.cstr_);
        std::swap(index_, other.index_);
    }

    Value::CZString &Value::CZString::operator=(const CZString &other) {
        cstr_ = other.cstr_;
        index_ = other.index_;
        return *this;
    }

    Value::CZString &Value::CZString::operator=(CZString &&other) noexcept {
        cstr_ = other.cstr_;
        index_ = other.index_;
        other.cstr_ = nullptr;
        return *this;
    }

    bool Value::CZString::operator<(const CZString &other) const {
        if (!cstr_)
            return index_ < other.index_;
        // return strcmp(cstr_, other.cstr_) < 0;
        // Assume both are strings.
        unsigned this_len = this->storage_.length_;
        unsigned other_len = other.storage_.length_;
        unsigned min_len = std::min<unsigned>(this_len, other_len);
        JSON_ASSERT(this->cstr_ && other.cstr_);
        int comp = memcmp(this->cstr_, other.cstr_, min_len);
        if (comp < 0)
            return true;
        if (comp > 0)
            return false;
        return (this_len < other_len);
    }

    bool Value::CZString::operator==(const CZString &other) const {
        if (!cstr_)
            return index_ == other.index_;
        // return strcmp(cstr_, other.cstr_) == 0;
        // Assume both are strings.
        unsigned this_len = this->storage_.length_;
        unsigned other_len = other.storage_.length_;
        if (this_len != other_len)
            return false;
        JSON_ASSERT(this->cstr_ && other.cstr_);
        int comp = memcmp(this->cstr_, other.cstr_, this_len);
        return comp == 0;
    }

    ArrayIndex Value::CZString::index() const { return index_; }

// const char* Value::CZString::c_str() const { return cstr_; }
    const char *Value::CZString::data() const { return cstr_; }

    unsigned Value::CZString::length() const { return storage_.length_; }

    bool Value::CZString::isStaticString() const {
        return storage_.policy_ == noDuplication;
    }

// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class Value::Value
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////

/*! \internal Default constructor initialization must be equivalent to:
 * memset( this, 0, sizeof(Value) )
 * This optimization is used in ValueInternalMap fast allocator.
 */
    Value::Value(ValueType type) {
        static char const emptyString[] = "";
        initBasic(type);
        switch (type) {
            case nullValue:
                break;
            case intValue:
            case uintValue:
                value_.int_ = 0;
                break;
            case realValue:
                value_.real_ = 0.0;
                break;
            case stringValue:
                // allocated_ == false, so this is safe.
                value_.string_ = const_cast<char *>(static_cast<char const *>(emptyString));
                break;
            case arrayValue:
            case objectValue:
                value_.map_ = new ObjectValues();
                break;
            case booleanValue:
                value_.bool_ = false;
                break;
            default:
                JSON_ASSERT_UNREACHABLE;
        }
    }

    Value::Value(Int value) {
        initBasic(intValue);
        value_.int_ = value;
    }

    Value::Value(UInt value) {
        initBasic(uintValue);
        value_.uint_ = value;
    }

#if defined(JSON_HAS_INT64)

    Value::Value(Int64 value) {
        initBasic(intValue);
        value_.int_ = value;
    }

    Value::Value(UInt64 value) {
        initBasic(uintValue);
        value_.uint_ = value;
    }

#endif // defined(JSON_HAS_INT64)

    Value::Value(double value) {
        initBasic(realValue);
        value_.real_ = value;
    }

    Value::Value(const char *value) {
        initBasic(stringValue, true);
        JSON_ASSERT_MESSAGE(value != nullptr,
                            "Null Value Passed to Value Constructor");
        value_.string_ = duplicateAndPrefixStringValue(
                value, static_cast<unsigned>(strlen(value)));
    }

    Value::Value(const char *begin, const char *end) {
        initBasic(stringValue, true);
        value_.string_ =
                duplicateAndPrefixStringValue(begin, static_cast<unsigned>(end - begin));
    }

    Value::Value(const String &value) {
        initBasic(stringValue, true);
        value_.string_ = duplicateAndPrefixStringValue(
                value.data(), static_cast<unsigned>(value.length()));
    }

    Value::Value(const StaticString &value) {
        initBasic(stringValue);
        value_.string_ = const_cast<char *>(value.c_str());
    }

    Value::Value(bool value) {
        initBasic(booleanValue);
        value_.bool_ = value;
    }

    Value::Value(const Value &other) {
        dupPayload(other);
        dupMeta(other);
    }

    Value::Value(Value &&other) noexcept {
        initBasic(nullValue);
        swap(other);
    }

    Value::~Value() {
        releasePayload();
        value_.uint_ = 0;
    }

    Value &Value::operator=(const Value &other) {
        Value(other).swap(*this);
        return *this;
    }

    Value &Value::operator=(Value &&other) noexcept {
        other.swap(*this);
        return *this;
    }

    void Value::swapPayload(Value &other) {
        std::swap(bits_, other.bits_);
        std::swap(value_, other.value_);
    }

    void Value::copyPayload(const Value &other) {
        releasePayload();
        dupPayload(other);
    }

    void Value::swap(Value &other) {
        swapPayload(other);
        std::swap(comments_, other.comments_);
        std::swap(start_, other.start_);
        std::swap(limit_, other.limit_);
    }

    void Value::copy(const Value &other) {
        copyPayload(other);
        dupMeta(other);
    }

    ValueType Value::type() const {
        return static_cast<ValueType>(bits_.value_type_);
    }

    int Value::compare(const Value &other) const {
        if (*this < other)
            return -1;
        if (*this > other)
            return 1;
        return 0;
    }

    bool Value::operator<(const Value &other) const {
        int typeDelta = type() - other.type();
        if (typeDelta)
            return typeDelta < 0;
        switch (type()) {
            case nullValue:
                return false;
            case intValue:
                return value_.int_ < other.value_.int_;
            case uintValue:
                return value_.uint_ < other.value_.uint_;
            case realValue:
                return value_.real_ < other.value_.real_;
            case booleanValue:
                return value_.bool_ < other.value_.bool_;
            case stringValue: {
                if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) {
                    return other.value_.string_ != nullptr;
                }
                unsigned this_len;
                unsigned other_len;
                char const *this_str;
                char const *other_str;
                decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
                                     &this_str);
                decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len,
                                     &other_str);
                unsigned min_len = std::min<unsigned>(this_len, other_len);
                JSON_ASSERT(this_str && other_str);
                int comp = memcmp(this_str, other_str, min_len);
                if (comp < 0)
                    return true;
                if (comp > 0)
                    return false;
                return (this_len < other_len);
            }
            case arrayValue:
            case objectValue: {
                auto thisSize = value_.map_->size();
                auto otherSize = other.value_.map_->size();
                if (thisSize != otherSize)
                    return thisSize < otherSize;
                return (*value_.map_) < (*other.value_.map_);
            }
            default:
                JSON_ASSERT_UNREACHABLE;
        }
        return false; // unreachable
    }

    bool Value::operator<=(const Value &other) const { return !(other < *this); }

    bool Value::operator>=(const Value &other) const { return !(*this < other); }

    bool Value::operator>(const Value &other) const { return other < *this; }

    bool Value::operator==(const Value &other) const {
        if (type() != other.type())
            return false;
        switch (type()) {
            case nullValue:
                return true;
            case intValue:
                return value_.int_ == other.value_.int_;
            case uintValue:
                return value_.uint_ == other.value_.uint_;
            case realValue:
                return value_.real_ == other.value_.real_;
            case booleanValue:
                return value_.bool_ == other.value_.bool_;
            case stringValue: {
                if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) {
                    return (value_.string_ == other.value_.string_);
                }
                unsigned this_len;
                unsigned other_len;
                char const *this_str;
                char const *other_str;
                decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
                                     &this_str);
                decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len,
                                     &other_str);
                if (this_len != other_len)
                    return false;
                JSON_ASSERT(this_str && other_str);
                int comp = memcmp(this_str, other_str, this_len);
                return comp == 0;
            }
            case arrayValue:
            case objectValue:
                return value_.map_->size() == other.value_.map_->size() &&
                       (*value_.map_) == (*other.value_.map_);
            default:
                JSON_ASSERT_UNREACHABLE;
        }
        return false; // unreachable
    }

    bool Value::operator!=(const Value &other) const { return !(*this == other); }

    const char *Value::asCString() const {
        JSON_ASSERT_MESSAGE(type() == stringValue,
                            "in Json::Value::asCString(): requires stringValue");
        if (value_.string_ == nullptr)
            return nullptr;
        unsigned this_len;
        char const *this_str;
        decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
                             &this_str);
        return this_str;
    }

#if JSONCPP_USING_SECURE_MEMORY
    unsigned Value::getCStringLength() const {
      JSON_ASSERT_MESSAGE(type() == stringValue,
                          "in Json::Value::asCString(): requires stringValue");
      if (value_.string_ == 0)
        return 0;
      unsigned this_len;
      char const* this_str;
      decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
                           &this_str);
      return this_len;
    }
#endif

    bool Value::getString(char const **begin, char const **end) const {
        if (type() != stringValue)
            return false;
        if (value_.string_ == nullptr)
            return false;
        unsigned length;
        decodePrefixedString(this->isAllocated(), this->value_.string_, &length,
                             begin);
        *end = *begin + length;
        return true;
    }

    String Value::asString() const {
        switch (type()) {
            case nullValue:
                return "";
            case stringValue: {
                if (value_.string_ == nullptr)
                    return "";
                unsigned this_len;
                char const *this_str;
                decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
                                     &this_str);
                return String(this_str, this_len);
            }
            case booleanValue:
                return value_.bool_ ? "true" : "false";
            case intValue:
                return valueToString(value_.int_);
            case uintValue:
                return valueToString(value_.uint_);
            case realValue:
                return valueToString(value_.real_);
            default:
                JSON_FAIL_MESSAGE("Type is not convertible to string");
        }
    }

    Value::Int Value::asInt() const {
        switch (type()) {
            case intValue:
                JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range");
                return Int(value_.int_);
            case uintValue:
                JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range");
                return Int(value_.uint_);
            case realValue:
                JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt),
                                    "double out of Int range");
                return Int(value_.real_);
            case nullValue:
                return 0;
            case booleanValue:
                return value_.bool_ ? 1 : 0;
            default:
                break;
        }
        JSON_FAIL_MESSAGE("Value is not convertible to Int.");
    }

    Value::UInt Value::asUInt() const {
        switch (type()) {
            case intValue:
                JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range");
                return UInt(value_.int_);
            case uintValue:
                JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range");
                return UInt(value_.uint_);
            case realValue:
                JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt),
                                    "double out of UInt range");
                return UInt(value_.real_);
            case nullValue:
                return 0;
            case booleanValue:
                return value_.bool_ ? 1 : 0;
            default:
                break;
        }
        JSON_FAIL_MESSAGE("Value is not convertible to UInt.");
    }

#if defined(JSON_HAS_INT64)

    Value::Int64 Value::asInt64() const {
        switch (type()) {
            case intValue:
                return Int64(value_.int_);
            case uintValue:
                JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range");
                return Int64(value_.uint_);
            case realValue:
                JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64),
                                    "double out of Int64 range");
                return Int64(value_.real_);
            case nullValue:
                return 0;
            case booleanValue:
                return value_.bool_ ? 1 : 0;
            default:
                break;
        }
        JSON_FAIL_MESSAGE("Value is not convertible to Int64.");
    }

    Value::UInt64 Value::asUInt64() const {
        switch (type()) {
            case intValue:
                JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range");
                return UInt64(value_.int_);
            case uintValue:
                return UInt64(value_.uint_);
            case realValue:
                JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64),
                                    "double out of UInt64 range");
                return UInt64(value_.real_);
            case nullValue:
                return 0;
            case booleanValue:
                return value_.bool_ ? 1 : 0;
            default:
                break;
        }
        JSON_FAIL_MESSAGE("Value is not convertible to UInt64.");
    }

#endif // if defined(JSON_HAS_INT64)

    LargestInt Value::asLargestInt() const {
#if defined(JSON_NO_INT64)
        return asInt();
#else
        return asInt64();
#endif
    }

    LargestUInt Value::asLargestUInt() const {
#if defined(JSON_NO_INT64)
        return asUInt();
#else
        return asUInt64();
#endif
    }

    double Value::asDouble() const {
        switch (type()) {
            case intValue:
                return static_cast<double>(value_.int_);
            case uintValue:
#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
                return static_cast<double>(value_.uint_);
#else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
                return integerToDouble(value_.uint_);
#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
            case realValue:
                return value_.real_;
            case nullValue:
                return 0.0;
            case booleanValue:
                return value_.bool_ ? 1.0 : 0.0;
            default:
                break;
        }
        JSON_FAIL_MESSAGE("Value is not convertible to double.");
    }

    float Value::asFloat() const {
        switch (type()) {
            case intValue:
                return static_cast<float>(value_.int_);
            case uintValue:
#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
                return static_cast<float>(value_.uint_);
#else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
                // This can fail (silently?) if the value is bigger than MAX_FLOAT.
                return static_cast<float>(integerToDouble(value_.uint_));
#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
            case realValue:
                return static_cast<float>(value_.real_);
            case nullValue:
                return 0.0;
            case booleanValue:
                return value_.bool_ ? 1.0F : 0.0F;
            default:
                break;
        }
        JSON_FAIL_MESSAGE("Value is not convertible to float.");
    }

    bool Value::asBool() const {
        switch (type()) {
            case booleanValue:
                return value_.bool_;
            case nullValue:
                return false;
            case intValue:
                return value_.int_ != 0;
            case uintValue:
                return value_.uint_ != 0;
            case realValue: {
                // According to JavaScript language zero or NaN is regarded as false
                const auto value_classification = std::fpclassify(value_.real_);
                return value_classification != FP_ZERO && value_classification != FP_NAN;
            }
            default:
                break;
        }
        JSON_FAIL_MESSAGE("Value is not convertible to bool.");
    }

    bool Value::isConvertibleTo(ValueType other) const {
        switch (other) {
            case nullValue:
                return (isNumeric() && asDouble() == 0.0) ||
                       (type() == booleanValue && !value_.bool_) ||
                       (type() == stringValue && asString().empty()) ||
                       (type() == arrayValue && value_.map_->empty()) ||
                       (type() == objectValue && value_.map_->empty()) ||
                       type() == nullValue;
            case intValue:
                return isInt() ||
                       (type() == realValue && InRange(value_.real_, minInt, maxInt)) ||
                       type() == booleanValue || type() == nullValue;
            case uintValue:
                return isUInt() ||
                       (type() == realValue && InRange(value_.real_, 0, maxUInt)) ||
                       type() == booleanValue || type() == nullValue;
            case realValue:
                return isNumeric() || type() == booleanValue || type() == nullValue;
            case booleanValue:
                return isNumeric() || type() == booleanValue || type() == nullValue;
            case stringValue:
                return isNumeric() || type() == booleanValue || type() == stringValue ||
                       type() == nullValue;
            case arrayValue:
                return type() == arrayValue || type() == nullValue;
            case objectValue:
                return type() == objectValue || type() == nullValue;
        }
        JSON_ASSERT_UNREACHABLE;
        return false;
    }

/// Number of values in array or object
    ArrayIndex Value::size() const {
        switch (type()) {
            case nullValue:
            case intValue:
            case uintValue:
            case realValue:
            case booleanValue:
            case stringValue:
                return 0;
            case arrayValue: // size of the array is highest index + 1
                if (!value_.map_->empty()) {
                    ObjectValues::const_iterator itLast = value_.map_->end();
                    --itLast;
                    return (*itLast).first.index() + 1;
                }
                return 0;
            case objectValue:
                return ArrayIndex(value_.map_->size());
        }
        JSON_ASSERT_UNREACHABLE;
        return 0; // unreachable;
    }

    bool Value::empty() const {
        if (isNull() || isArray() || isObject())
            return size() == 0U;
        return false;
    }

    Value::operator bool() const { return !isNull(); }

    void Value::clear() {
        JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue ||
                            type() == objectValue,
                            "in Json::Value::clear(): requires complex value");
        start_ = 0;
        limit_ = 0;
        switch (type()) {
            case arrayValue:
            case objectValue:
                value_.map_->clear();
                break;
            default:
                break;
        }
    }

    void Value::resize(ArrayIndex newSize) {
        JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
                            "in Json::Value::resize(): requires arrayValue");
        if (type() == nullValue)
            *this = Value(arrayValue);
        ArrayIndex oldSize = size();
        if (newSize == 0)
            clear();
        else if (newSize > oldSize)
            for (ArrayIndex i = oldSize; i < newSize; ++i)
                (*this)[i];
        else {
            for (ArrayIndex index = newSize; index < oldSize; ++index) {
                value_.map_->erase(index);
            }
            JSON_ASSERT(size() == newSize);
        }
    }

    Value &Value::operator[](ArrayIndex index) {
        JSON_ASSERT_MESSAGE(
                type() == nullValue || type() == arrayValue,
                "in Json::Value::operator[](ArrayIndex): requires arrayValue");
        if (type() == nullValue)
            *this = Value(arrayValue);
        CZString key(index);
        auto it = value_.map_->lower_bound(key);
        if (it != value_.map_->end() && (*it).first == key)
            return (*it).second;

        ObjectValues::value_type defaultValue(key, nullSingleton());
        it = value_.map_->insert(it, defaultValue);
        return (*it).second;
    }

    Value &Value::operator[](int index) {
        JSON_ASSERT_MESSAGE(
                index >= 0,
                "in Json::Value::operator[](int index): index cannot be negative");
        return (*this)[ArrayIndex(index)];
    }

    const Value &Value::operator[](ArrayIndex index) const {
        JSON_ASSERT_MESSAGE(
                type() == nullValue || type() == arrayValue,
                "in Json::Value::operator[](ArrayIndex)const: requires arrayValue");
        if (type() == nullValue)
            return nullSingleton();
        CZString key(index);
        ObjectValues::const_iterator it = value_.map_->find(key);
        if (it == value_.map_->end())
            return nullSingleton();
        return (*it).second;
    }

    const Value &Value::operator[](int index) const {
        JSON_ASSERT_MESSAGE(
                index >= 0,
                "in Json::Value::operator[](int index) const: index cannot be negative");
        return (*this)[ArrayIndex(index)];
    }

    void Value::initBasic(ValueType type, bool allocated) {
        setType(type);
        setIsAllocated(allocated);
        comments_ = Comments{};
        start_ = 0;
        limit_ = 0;
    }

    void Value::dupPayload(const Value &other) {
        setType(other.type());
        setIsAllocated(false);
        switch (type()) {
            case nullValue:
            case intValue:
            case uintValue:
            case realValue:
            case booleanValue:
                value_ = other.value_;
                break;
            case stringValue:
                if (other.value_.string_ && other.isAllocated()) {
                    unsigned len;
                    char const *str;
                    decodePrefixedString(other.isAllocated(), other.value_.string_, &len,
                                         &str);
                    value_.string_ = duplicateAndPrefixStringValue(str, len);
                    setIsAllocated(true);
                } else {
                    value_.string_ = other.value_.string_;
                }
                break;
            case arrayValue:
            case objectValue:
                value_.map_ = new ObjectValues(*other.value_.map_);
                break;
            default:
                JSON_ASSERT_UNREACHABLE;
        }
    }

    void Value::releasePayload() {
        switch (type()) {
            case nullValue:
            case intValue:
            case uintValue:
            case realValue:
            case booleanValue:
                break;
            case stringValue:
                if (isAllocated())
                    releasePrefixedStringValue(value_.string_);
                break;
            case arrayValue:
            case objectValue:
                delete value_.map_;
                break;
            default:
                JSON_ASSERT_UNREACHABLE;
        }
    }

    void Value::dupMeta(const Value &other) {
        comments_ = other.comments_;
        start_ = other.start_;
        limit_ = other.limit_;
    }

// Access an object value by name, create a null member if it does not exist.
// @pre Type of '*this' is object or null.
// @param key is null-terminated.
    Value &Value::resolveReference(const char *key) {
        JSON_ASSERT_MESSAGE(
                type() == nullValue || type() == objectValue,
                "in Json::Value::resolveReference(): requires objectValue");
        if (type() == nullValue)
            *this = Value(objectValue);
        CZString actualKey(key, static_cast<unsigned>(strlen(key)),
                           CZString::noDuplication); // NOTE!
        auto it = value_.map_->lower_bound(actualKey);
        if (it != value_.map_->end() && (*it).first == actualKey)
            return (*it).second;

        ObjectValues::value_type defaultValue(actualKey, nullSingleton());
        it = value_.map_->insert(it, defaultValue);
        Value &value = (*it).second;
        return value;
    }

// @param key is not null-terminated.
    Value &Value::resolveReference(char const *key, char const *end) {
        JSON_ASSERT_MESSAGE(
                type() == nullValue || type() == objectValue,
                "in Json::Value::resolveReference(key, end): requires objectValue");
        if (type() == nullValue)
            *this = Value(objectValue);
        CZString actualKey(key, static_cast<unsigned>(end - key),
                           CZString::duplicateOnCopy);
        auto it = value_.map_->lower_bound(actualKey);
        if (it != value_.map_->end() && (*it).first == actualKey)
            return (*it).second;

        ObjectValues::value_type defaultValue(actualKey, nullSingleton());
        it = value_.map_->insert(it, defaultValue);
        Value &value = (*it).second;
        return value;
    }

    Value Value::get(ArrayIndex index, const Value &defaultValue) const {
        const Value *value = &((*this)[index]);
        return value == &nullSingleton() ? defaultValue : *value;
    }

    bool Value::isValidIndex(ArrayIndex index) const { return index < size(); }

    Value const *Value::find(char const *begin, char const *end) const {
        JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
                            "in Json::Value::find(begin, end): requires "
                            "objectValue or nullValue");
        if (type() == nullValue)
            return nullptr;
        CZString actualKey(begin, static_cast<unsigned>(end - begin),
                           CZString::noDuplication);
        ObjectValues::const_iterator it = value_.map_->find(actualKey);
        if (it == value_.map_->end())
            return nullptr;
        return &(*it).second;
    }

    Value *Value::demand(char const *begin, char const *end) {
        JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
                            "in Json::Value::demand(begin, end): requires "
                            "objectValue or nullValue");
        return &resolveReference(begin, end);
    }

    const Value &Value::operator[](const char *key) const {
        Value const *found = find(key, key + strlen(key));
        if (!found)
            return nullSingleton();
        return *found;
    }

    Value const &Value::operator[](const String &key) const {
        Value const *found = find(key.data(), key.data() + key.length());
        if (!found)
            return nullSingleton();
        return *found;
    }

    Value &Value::operator[](const char *key) {
        return resolveReference(key, key + strlen(key));
    }

    Value &Value::operator[](const String &key) {
        return resolveReference(key.data(), key.data() + key.length());
    }

    Value &Value::operator[](const StaticString &key) {
        return resolveReference(key.c_str());
    }

    Value &Value::append(const Value &value) { return append(Value(value)); }

    Value &Value::append(Value &&value) {
        JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
                            "in Json::Value::append: requires arrayValue");
        if (type() == nullValue) {
            *this = Value(arrayValue);
        }
        return this->value_.map_->emplace(size(), std::move(value)).first->second;
    }

    bool Value::insert(ArrayIndex index, const Value &newValue) {
        return insert(index, Value(newValue));
    }

    bool Value::insert(ArrayIndex index, Value &&newValue) {
        JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
                            "in Json::Value::insert: requires arrayValue");
        ArrayIndex length = size();
        if (index > length) {
            return false;
        }
        for (ArrayIndex i = length; i > index; i--) {
            (*this)[i] = std::move((*this)[i - 1]);
        }
        (*this)[index] = std::move(newValue);
        return true;
    }

    Value Value::get(char const *begin, char const *end,
                     Value const &defaultValue) const {
        Value const *found = find(begin, end);
        return !found ? defaultValue : *found;
    }

    Value Value::get(char const *key, Value const &defaultValue) const {
        return get(key, key + strlen(key), defaultValue);
    }

    Value Value::get(String const &key, Value const &defaultValue) const {
        return get(key.data(), key.data() + key.length(), defaultValue);
    }

    bool Value::removeMember(const char *begin, const char *end, Value *removed) {
        if (type() != objectValue) {
            return false;
        }
        CZString actualKey(begin, static_cast<unsigned>(end - begin),
                           CZString::noDuplication);
        auto it = value_.map_->find(actualKey);
        if (it == value_.map_->end())
            return false;
        if (removed)
            *removed = std::move(it->second);
        value_.map_->erase(it);
        return true;
    }

    bool Value::removeMember(const char *key, Value *removed) {
        return removeMember(key, key + strlen(key), removed);
    }

    bool Value::removeMember(String const &key, Value *removed) {
        return removeMember(key.data(), key.data() + key.length(), removed);
    }

    void Value::removeMember(const char *key) {
        JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
                            "in Json::Value::removeMember(): requires objectValue");
        if (type() == nullValue)
            return;

        CZString actualKey(key, unsigned(strlen(key)), CZString::noDuplication);
        value_.map_->erase(actualKey);
    }

    void Value::removeMember(const String &key) { removeMember(key.c_str()); }

    bool Value::removeIndex(ArrayIndex index, Value *removed) {
        if (type() != arrayValue) {
            return false;
        }
        CZString key(index);
        auto it = value_.map_->find(key);
        if (it == value_.map_->end()) {
            return false;
        }
        if (removed)
            *removed = it->second;
        ArrayIndex oldSize = size();
        // shift left all items left, into the place of the "removed"
        for (ArrayIndex i = index; i < (oldSize - 1); ++i) {
            CZString keey(i);
            (*value_.map_)[keey] = (*this)[i + 1];
        }
        // erase the last one ("leftover")
        CZString keyLast(oldSize - 1);
        auto itLast = value_.map_->find(keyLast);
        value_.map_->erase(itLast);
        return true;
    }

    bool Value::isMember(char const *begin, char const *end) const {
        Value const *value = find(begin, end);
        return nullptr != value;
    }

    bool Value::isMember(char const *key) const {
        return isMember(key, key + strlen(key));
    }

    bool Value::isMember(String const &key) const {
        return isMember(key.data(), key.data() + key.length());
    }

    Value::Members Value::getMemberNames() const {
        JSON_ASSERT_MESSAGE(
                type() == nullValue || type() == objectValue,
                "in Json::Value::getMemberNames(), value must be objectValue");
        if (type() == nullValue)
            return Value::Members();
        Members members;
        members.reserve(value_.map_->size());
        ObjectValues::const_iterator it = value_.map_->begin();
        ObjectValues::const_iterator itEnd = value_.map_->end();
        for (; it != itEnd; ++it) {
            members.push_back(String((*it).first.data(), (*it).first.length()));
        }
        return members;
    }

    static bool IsIntegral(double d) {
        double integral_part;
        return modf(d, &integral_part) == 0.0;
    }

    bool Value::isNull() const { return type() == nullValue; }

    bool Value::isBool() const { return type() == booleanValue; }

    bool Value::isInt() const {
        switch (type()) {
            case intValue:
#if defined(JSON_HAS_INT64)
                return value_.int_ >= minInt && value_.int_ <= maxInt;
#else
                return true;
#endif
            case uintValue:
                return value_.uint_ <= UInt(maxInt);
            case realValue:
                return value_.real_ >= minInt && value_.real_ <= maxInt &&
                       IsIntegral(value_.real_);
            default:
                break;
        }
        return false;
    }

    bool Value::isUInt() const {
        switch (type()) {
            case intValue:
#if defined(JSON_HAS_INT64)
                return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt);
#else
                return value_.int_ >= 0;
#endif
            case uintValue:
#if defined(JSON_HAS_INT64)
                return value_.uint_ <= maxUInt;
#else
                return true;
#endif
            case realValue:
                return value_.real_ >= 0 && value_.real_ <= maxUInt &&
                       IsIntegral(value_.real_);
            default:
                break;
        }
        return false;
    }

    bool Value::isInt64() const {
#if defined(JSON_HAS_INT64)
        switch (type()) {
            case intValue:
                return true;
            case uintValue:
                return value_.uint_ <= UInt64(maxInt64);
            case realValue:
                // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a
                // double, so double(maxInt64) will be rounded up to 2^63. Therefore we
                // require the value to be strictly less than the limit.
                return value_.real_ >= double(minInt64) &&
                       value_.real_ < double(maxInt64) && IsIntegral(value_.real_);
            default:
                break;
        }
#endif // JSON_HAS_INT64
        return false;
    }

    bool Value::isUInt64() const {
#if defined(JSON_HAS_INT64)
        switch (type()) {
            case intValue:
                return value_.int_ >= 0;
            case uintValue:
                return true;
            case realValue:
                // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
                // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
                // require the value to be strictly less than the limit.
                return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble &&
                       IsIntegral(value_.real_);
            default:
                break;
        }
#endif // JSON_HAS_INT64
        return false;
    }

    bool Value::isIntegral() const {
        switch (type()) {
            case intValue:
            case uintValue:
                return true;
            case realValue:
#if defined(JSON_HAS_INT64)
                // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
                // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
                // require the value to be strictly less than the limit.
                return value_.real_ >= double(minInt64) &&
                       value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_);
#else
                return value_.real_ >= minInt && value_.real_ <= maxUInt &&
                       IsIntegral(value_.real_);
#endif // JSON_HAS_INT64
            default:
                break;
        }
        return false;
    }

    bool Value::isDouble() const {
        return type() == intValue || type() == uintValue || type() == realValue;
    }

    bool Value::isNumeric() const { return isDouble(); }

    bool Value::isString() const { return type() == stringValue; }

    bool Value::isArray() const { return type() == arrayValue; }

    bool Value::isObject() const { return type() == objectValue; }

    Value::Comments::Comments(const Comments &that)
            : ptr_{cloneUnique(that.ptr_)} {}

    Value::Comments::Comments(Comments &&that) noexcept
            : ptr_{std::move(that.ptr_)} {}

    Value::Comments &Value::Comments::operator=(const Comments &that) {
        ptr_ = cloneUnique(that.ptr_);
        return *this;
    }

    Value::Comments &Value::Comments::operator=(Comments &&that) noexcept {
        ptr_ = std::move(that.ptr_);
        return *this;
    }

    bool Value::Comments::has(CommentPlacement slot) const {
        return ptr_ && !(*ptr_)[slot].empty();
    }

    String Value::Comments::get(CommentPlacement slot) const {
        if (!ptr_)
            return {};
        return (*ptr_)[slot];
    }

    void Value::Comments::set(CommentPlacement slot, String comment) {
        if (slot >= CommentPlacement::numberOfCommentPlacement)
            return;
        if (!ptr_)
            ptr_ = std::unique_ptr<Array>(new Array());
        (*ptr_)[slot] = std::move(comment);
    }

    void Value::setComment(String comment, CommentPlacement placement) {
        if (!comment.empty() && (comment.back() == '\n')) {
            // Always discard trailing newline, to aid indentation.
            comment.pop_back();
        }
        JSON_ASSERT(!comment.empty());
        JSON_ASSERT_MESSAGE(
                comment[0] == '\0' || comment[0] == '/',
                "in Json::Value::setComment(): Comments must start with /");
        comments_.set(placement, std::move(comment));
    }

    bool Value::hasComment(CommentPlacement placement) const {
        return comments_.has(placement);
    }

    String Value::getComment(CommentPlacement placement) const {
        return comments_.get(placement);
    }

    void Value::setOffsetStart(ptrdiff_t start) { start_ = start; }

    void Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; }

    ptrdiff_t Value::getOffsetStart() const { return start_; }

    ptrdiff_t Value::getOffsetLimit() const { return limit_; }

    String Value::toStyledString() const {
        StreamWriterBuilder builder;

        String out = this->hasComment(commentBefore) ? "\n" : "";
        out += Json::writeString(builder, *this);
        out += '\n';

        return out;
    }

    Value::const_iterator Value::begin() const {
        switch (type()) {
            case arrayValue:
            case objectValue:
                if (value_.map_)
                    return const_iterator(value_.map_->begin());
                break;
            default:
                break;
        }
        return {};
    }

    Value::const_iterator Value::end() const {
        switch (type()) {
            case arrayValue:
            case objectValue:
                if (value_.map_)
                    return const_iterator(value_.map_->end());
                break;
            default:
                break;
        }
        return {};
    }

    Value::iterator Value::begin() {
        switch (type()) {
            case arrayValue:
            case objectValue:
                if (value_.map_)
                    return iterator(value_.map_->begin());
                break;
            default:
                break;
        }
        return iterator();
    }

    Value::iterator Value::end() {
        switch (type()) {
            case arrayValue:
            case objectValue:
                if (value_.map_)
                    return iterator(value_.map_->end());
                break;
            default:
                break;
        }
        return iterator();
    }

// class PathArgument
// //////////////////////////////////////////////////////////////////

    PathArgument::PathArgument() = default;

    PathArgument::PathArgument(ArrayIndex index)
            : index_(index), kind_(kindIndex) {}

    PathArgument::PathArgument(const char *key) : key_(key), kind_(kindKey) {}

    PathArgument::PathArgument(String key) : key_(std::move(key)), kind_(kindKey) {}

// class Path
// //////////////////////////////////////////////////////////////////

    Path::Path(const String &path, const PathArgument &a1, const PathArgument &a2,
               const PathArgument &a3, const PathArgument &a4,
               const PathArgument &a5) {
        InArgs in;
        in.reserve(5);
        in.push_back(&a1);
        in.push_back(&a2);
        in.push_back(&a3);
        in.push_back(&a4);
        in.push_back(&a5);
        makePath(path, in);
    }

    void Path::makePath(const String &path, const InArgs &in) {
        const char *current = path.c_str();
        const char *end = current + path.length();
        auto itInArg = in.begin();
        while (current != end) {
            if (*current == '[') {
                ++current;
                if (*current == '%')
                    addPathInArg(path, in, itInArg, PathArgument::kindIndex);
                else {
                    ArrayIndex index = 0;
                    for (; current != end && *current >= '0' && *current <= '9'; ++current)
                        index = index * 10 + ArrayIndex(*current - '0');
                    args_.push_back(index);
                }
                if (current == end || *++current != ']')
                    invalidPath(path, int(current - path.c_str()));
            } else if (*current == '%') {
                addPathInArg(path, in, itInArg, PathArgument::kindKey);
                ++current;
            } else if (*current == '.' || *current == ']') {
                ++current;
            } else {
                const char *beginName = current;
                while (current != end && !strchr("[.", *current))
                    ++current;
                args_.push_back(String(beginName, current));
            }
        }
    }

    void Path::addPathInArg(const String & /*path*/, const InArgs &in,
                            InArgs::const_iterator &itInArg,
                            PathArgument::Kind kind) {
        if (itInArg == in.end()) {
            // Error: missing argument %d
        } else if ((*itInArg)->kind_ != kind) {
            // Error: bad argument type
        } else {
            args_.push_back(**itInArg++);
        }
    }

    void Path::invalidPath(const String & /*path*/, int /*location*/) {
        // Error: invalid path.
    }

    const Value &Path::resolve(const Value &root) const {
        const Value *node = &root;
        for (const auto &arg : args_) {
            if (arg.kind_ == PathArgument::kindIndex) {
                if (!node->isArray() || !node->isValidIndex(arg.index_)) {
                    // Error: unable to resolve path (array value expected at position... )
                    return Value::nullSingleton();
                }
                node = &((*node)[arg.index_]);
            } else if (arg.kind_ == PathArgument::kindKey) {
                if (!node->isObject()) {
                    // Error: unable to resolve path (object value expected at position...)
                    return Value::nullSingleton();
                }
                node = &((*node)[arg.key_]);
                if (node == &Value::nullSingleton()) {
                    // Error: unable to resolve path (object has no member named '' at
                    // position...)
                    return Value::nullSingleton();
                }
            }
        }
        return *node;
    }

    Value Path::resolve(const Value &root, const Value &defaultValue) const {
        const Value *node = &root;
        for (const auto &arg : args_) {
            if (arg.kind_ == PathArgument::kindIndex) {
                if (!node->isArray() || !node->isValidIndex(arg.index_))
                    return defaultValue;
                node = &((*node)[arg.index_]);
            } else if (arg.kind_ == PathArgument::kindKey) {
                if (!node->isObject())
                    return defaultValue;
                node = &((*node)[arg.key_]);
                if (node == &Value::nullSingleton())
                    return defaultValue;
            }
        }
        return *node;
    }

    Value &Path::make(Value &root) const {
        Value *node = &root;
        for (const auto &arg : args_) {
            if (arg.kind_ == PathArgument::kindIndex) {
                if (!node->isArray()) {
                    // Error: node is not an array at position ...
                }
                node = &((*node)[arg.index_]);
            } else if (arg.kind_ == PathArgument::kindKey) {
                if (!node->isObject()) {
                    // Error: node is not an object at position...
                }
                node = &((*node)[arg.key_]);
            }
        }
        return *node;
    }

} // namespace Json
